﻿#include "tests.hpp"
#include <iomanip>
#include <iostream>
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
#endif

namespace miragon {

TestCase::TestCase(void) : name(), routine()
{
    TestRunner::instance().add(*this);
}

TestCase::TestCase(const std::string& name, TestRoutine routine)
    : name(name), routine(routine)
{
    TestRunner::instance().add(*this);
}

TestRunner::TestRunner(void) : mTestCases(), mLastAsserts()
{
}

void TestRunner::add(const TestCase& t)
{
    mTestCases.push_back(t);
}

int TestRunner::runAll(void)
{
    int current = 1;
    int failures = 0;

    std::cout << "Testing ..." << std::endl;
    std::cout << std::string(80, '=') << std::endl;
    for (auto t : mTestCases) {
        // Clear assert first
        mLastAsserts.clear();
        // Execute the test case routine
        t.routine();
        //
        bool passed = mLastAsserts.size() == 0;
        std::cout << std::right << std::setw(4) << current << "/" << std::left
                  << std::setw(4) << mTestCases.size() << " : " << t.name
                  << (passed ? " [passed]" : " [failed]") << std::endl;
        for (auto a : mLastAsserts) {
            std::cout << "          ? \"" << a << "\" [failed]" << std::endl;
        }
        failures += passed ? 0 : 1;
        current++;
    }
    std::cout << std::string(80, '=') << std::endl;
    std::cout << "Total: " << mTestCases.size()
              << ", Passes: " << mTestCases.size() - failures
              << ", Failures: " << failures << std::endl;

#if defined(_WIN32) || defined(_WIN64)
    system("pause");
#endif

    return failures;
}

TestCaseList& TestRunner::testCases(void)
{
    return mTestCases;
}

TestCaseAssertList& TestRunner::lastAsserts(void)
{
    return mLastAsserts;
}

TestRunner& TestRunner::instance(void)
{
    static TestRunner sTestRunner;
    return sTestRunner;
}

template <>
void expect_equal<int>(const int& a, const int& b,
    const char* expr)
{
    if (a != b)
        TestRunner::instance().lastAsserts().push_back(expr);
}

template <>
void expect_equal<uint32_t>(const uint32_t& a, const uint32_t& b,
                            const char* expr)
{
    if (a != b)
        TestRunner::instance().lastAsserts().push_back(expr);
}

template <>
void expect_equal<uint64_t>(const uint64_t& a, const uint64_t& b,
                            const char* expr)
{
    if (a != b)
        TestRunner::instance().lastAsserts().push_back(expr);
}

template <>
void expect_equal<float>(const float& a, const float& b, const char* expr)
{
    if (a != b)
        TestRunner::instance().lastAsserts().push_back(expr);
}

template <>
void expect_equal<double>(const double& a, const double& b, const char* expr)
{
    if (a != b)
        TestRunner::instance().lastAsserts().push_back(expr);
}

template <>
void expect_equal<std::string>(const std::string& a, const std::string& b,
                               const char* expr)
{
    if (a != b)
        TestRunner::instance().lastAsserts().push_back(expr);
}

void expect_starts_with(const std::string& target, const std::string& pattern,
                        const char* expr)
{
    if (target.compare(0, pattern.size(), pattern) != 0)
        TestRunner::instance().lastAsserts().push_back(expr);
}

} // namespace miragon

using miragon::TestRunner;

int main(int argc, char** argv)
{
    return TestRunner::instance().runAll();
}